#Module contains set of functions for WebApplications, Managed Paths, Solutions, Sites, and Features
function Install-Project ($configFile)
{
	Write-Header "SharePoint Solution Installation Script"
	Write-Host -f Green "---------------------------------------"
	#Get the xml configuration file
	[xml] $config = Get-Content $configFile
	
	Create-WebApplications
	Create-ManagedPaths
	Deploy-Solutions	
	Create-Sites
	Deploy-SandboxSolutions
	Enable-Feature
}

# Checks the web applications - and creates if needed
function Create-WebApplications ()
{
	function Create-SubWebApp ($webAppList)
	{
		begin {
			Write-Header "Creating Web Applications"
		}
		process {			
			$webAppUrl = $_.Url
			$webApp = Get-SPWebApplication | ? {$_.Url -eq ($webAppUrl + "/") }
			if ($webApp -eq $null)
			{
				Write-Host "Creating new web application for" $webAppUrl
				$appPool = Get-SPManagedAccount $_.ApplicationPoolAccount
				$hostHeader = $_.Url.Remove(0, $_.Url.LastIndexOf('/')+1)				
				New-SPWebApplication -Name $_.Name -URL $_.Url -HostHeader $hostHeader -Port 80 -DatabaseName ("WSS_Content_" + $_.Name) -ApplicationPool ("SharePoint - " + $_.Name) -ApplicationPoolAccount $appPool -Confirm:$false
			}
			else
			{
				Write-Host "Using existing web application for " $_.Url
			}			
		}
		end {
			Write-Footer "Completed web applications"
		}
	}
	
	if ($config.SPInstaller.WebApplications.ChildNodes.Count -gt 0)
	{	
		$config.SPInstaller.WebApplications.WebApplication |  Create-SubWebApp
	}
}

# Checks the managed paths, and creates them if they don't exist
function Create-ManagedPaths ()
{
	function Create-SubManagedPath ($mangedPathList)
	{
		begin {
			Write-Header "Creating managed paths"
		}
		process {	
			$webAppName = $_.WebApplication			
			$webApp = $config.SPInstaller.WebApplications.WebApplication | ? {$_.Name -eq $webAppName}
			$managedPathName = $_.Name
			$managedPath = Get-SPManagedPath -WebApplication $webApp.Url | ? {$_.Name -eq $managedPathName}
			if ($managedPath -eq $null)
			{
				Write-Host "Creating new managed path " $_.Name "on" $_.WebApplication
				$explicit = ($_.Type -eq 'Explicit')				
				New-SPManagedPath -RelativeURL $_.Url -WebApplication $webApp.Url -Explicit:$explicit
			}
			else
			{
				Write-Host "Using existing managed path" $_.Name " from " $_.WebApplication
			}
		}
		end {
			Write-Footer "Completed managed paths"
		}
	}
	
	if ($config.SPInstaller.ManagedPaths.ChildNodes.Count -gt 0)
	{
		$config.SPInstaller.ManagedPaths.ManagedPath | Create-SubManagedPath	
	}
}

#Solutions
function Find-Solutions ($farm)
{
	$list = @()
	foreach ($solution in $config.SPInstaller.Solutions.Solution)
	{			
		if ($farm -eq $true -and ($solution.Type -eq $null -or $solution.Type -ne "Sandbox"))
		{			
			$list += $solution.Name
		}
		if ($farm -eq $false -and $solution.Type -ne $null -and $solution.Type -eq "Sandbox")
		{
			$list += $solution.Name
		}
	}
	return $list
}

# Adds the solution into the solution store
function Deploy-Solutions ()
{
	#Get the list of farm solutions	
	$wspList = @()
	if ($config.SPInstaller.Solutions.AutoFind -eq "false")
	{
		$wspList = Find-Solutions ($true)				
	}
	else
	{
		$fileList = Get-ChildItem *.wsp
		foreach ($file in $fileList) {
			$wspList += $file.Name
		}
	}
	
	#Add or upgrade the farm solutions
	if ($wspList -ne $null)
	{	
		if ($config.SPInstaller.Solutions.Activate -ne $null)
		{
			switch ($config.SPInstaller.Solutions.Activate)
			{
				"Reinstall" {
					#Uninstall them from the farm
					$wspList | Uninstall-Solution
					$wspList | Remove-Solution
					
					#Add WSPs into the farm
					$wspList | Add-Solution
					$wspList | Install-Solution
				}
				"Upgrade" {
					$wspList | Upgrade-Solution					
				}
			}
		}
	}
	
	#Flush blob cache
	FlushWebApp
}

function Deploy-SandboxSolutions ()
{
	#Get the sandbox solutions
	$sandboxList = @()
		
	$sandboxList = Find-Solutions ($false)					
	
	#Add the sandbox solutiong
	$sandboxList | Add-Sandbox
	
	
}

# Flushes the blob cache on all web applications
function FlushWebApp ()
{
	foreach ($webApp in $config.SPInstaller.WebApplications.WebApplication)
	{		
		$webApp = Get-SPWebApplication $webApp.Url
		[Microsoft.SharePoint.Publishing.PublishingCache]::FlushBlobCache($webApp)
		Write-Header ("Flushed the BLOB cache for " + $webApp.Url)
	}
}

#Waits for all solution timer jobs to complete
function WaitForJobsToFinish()
{ 
    $JobName = "solution*"    
	
	$job = Get-SPTimerJob | ?{ $_.Name -like $JobName }		
	
	Write-Host -f Green "Waiting for all solution timer jobs to complete"
	while (($job = Get-SPTimerJob | ?{ $_.Name -like $JobName} | Sort-Object "Last Run" ) -ne $null)     
    {				
		foreach ($timerjob in $job)
		{
			$JobFullName = $timerjob.Name
			Write-Host -NoNewLine "Waiting to finish timer job $JobFullName"        
			while ((Get-SPTimerJob $JobFullName) -ne $null)
			{
				Write-Host -f Green -NoNewLine .
				Start-Sleep -Seconds 3				
			}
			Write-Host  -f Green "....Done"		
		}
		Start-Sleep -Seconds 2
		Write-Host ""
		Write-Host -f Green "Re-checking for jobs"
		       
    }
	Write-Host  -f Green ".... All Done"
}

#Uninstalls a list of WSPs
function Uninstall-Solution ($wspList)
{
	begin {
		Write-Header "Uninstalling any existing WSP's from the farm solution store"
	}
	process {
		$solName = $_
		$sol = Get-SPSolution -Identity $solName -ErrorAction SilentlyContinue -ErrorVariable err
		if ($sol -ne $null)
		{
			#Check if deployed or not
			if ($sol.Deployed -eq "True")
			{
				if ($sol.ContainsWebApplicationResource -eq "True")
				{
					Write-Host "Uninstalling $solName from the web applications"				
					Uninstall-SPSolution -Identity $solName -AllWebApplications -Confirm:$false 	
				}
				else
				{
					Write-Host "Uninstalling $solName from the farm"
					Uninstall-SPSolution -Identity $solName -Confirm:$false
				}
			}
		}
	}
	end {
		WaitForJobsToFinish
		Write-Footer "Finished uninstalling existing WSP's"
	}
}

#Removes a list of WSPs
function Remove-Solution ($wspList)
{
	begin {
		Write-Header "Removing WSP's from the farm solution store"
	}
	process {
		$solName = $_
		$sol = Get-SPSolution -Identity $solName -ErrorAction SilentlyContinue -ErrorVariable err
		if ($sol -ne $null)
		{
			Write-Host "Removing $_ from the farm"
			Remove-SPSolution -Identity $solName -Confirm:$false 
		}
	}
	end {
		Write-Footer "Finished removing existing WSP's"
	}
}

#Installs a solution, and removes it if its been previously installed
function Add-Solution ($wspList)
{
	begin {
		Write-Header "Adding WSP's into the farm solution store"
	}
	process {
		$solName = $_
		Write-Host "Adding solution $solName"
		Add-SPSolution -LiteralPath "$(gl)\$solName"  -ErrorAction SilentlyContinue -ErrorVariable err
		if ($err -ne $null)
		{
			Write-Host -f Red "Error occured when adding solution $solName"
			Write-Host -f Red "Error details: $err"
		}
	}
	end {
		Write-Footer "Finished adding new WSP's"
	}	
}

function Install-Solution ($wspList)
{
	begin {
		Write-Header "Installing WSP's into the farm solution store"
	}
	process {
		$solName = $_
		$solXml = $config.SPInstaller.Solutions.Solution | ? {$_.Name -eq $solName}
		Write-Host "Installing solution $solName"
		
		$sol = Get-SPSolution $solName
		if ($sol -ne $null)
		{
			if ($sol.ContainsWebApplicationResource)
			{		
				
				if ($solXml.WebApplication -ne $null)
				{					
					$webAppList = $solXml.WebApplication.Split(',')
					foreach ($webApp in $webAppList)
					{
						if ($webApp -ne $null -and $webApp -ne "")
						{							
							$webAppEntry = $config.SPInstaller.WebApplications.WebApplication | ? {$_.Name -eq $webApp}
							if ($webAppEntry -ne $null)
							{
								Write-Host "Installing $solName onto $webApp Application on" $webAppEntry.Url
								Install-SPSolution -Identity $solName -GACDeployment -WebApplication $webAppEntry.Url -force
							}
						}
					}
				}
				else
				{
					#Auto install - no explicit solution element
					if ($config.SPInstaller.Solutions.WebApplications -ne $null)
					{						
						$webAppList = $config.SPInstaller.Solutions.WebApplications.Split(',')						
						foreach ($webApp in $webAppList)
						{
							if ($webApp -ne $null -and $webApp -ne "")
							{							
								$webAppEntry = $config.SPInstaller.WebApplications.WebApplication | ? {$_.Name -eq $webApp}								
								if ($webAppEntry -ne $null)
								{
									Write-Host "Installing $solName onto $webApp Application on"  $webAppEntry.Url
									Install-SPSolution -Identity $solName -GACDeployment -WebApplication $webAppEntry.Url -force
								}
							}
						}
					}
					else
					{
						Write-Host "Installing $solName onto all web applications"
						Install-SPSolution -Identity $solName -GACDeployment -AllWebApplications -force
					}
				}
			}
			else
			{
				Write-Host "Installing $solName onto farm"				
				Install-SPSolution -Identity $solName -GACDeployment -force
			}
		}		
	}
	end {
		WaitForJobsToFinish
		Write-Footer "Finished installing new WSP's"
	}	
}

function Upgrade-Solution ($wspList)
{
	begin {
		Write-Header "Upgrading WSP's into the farm solution store"
	}
	process {
		$solName = $_		
		
		$sol = Get-SPSolution $solName
		if ($sol -ne $null)
		{			
				Write-Host "Upgrade solution $solName"		
				Update-SPSolution -Identity  $solName -LiteralPath "$(gl)\$solName" -GACDeployment -Force			
		}		
	}
	end {
		Write-Footer "Finished upgrading WSP's in the farm solution store"
	}	
}

#Waits for all solution timer jobs to complete
function WaitForJobsToFinish()
{ 
	$JobName = "solution*"    
	
	$job = Get-SPTimerJob | ?{ $_.Name -like $JobName }	
	
	if ($job -ne $null)
	{

		Write-Header "Waiting for all solution timer jobs to complete"
		while (($job = Get-SPTimerJob | ?{ $_.Name -like $JobName} | Sort-Object "Last Run" ) -ne $null)     
		{				
			foreach ($timerjob in $job)
			{
				$JobFullName = $timerjob.Name
				Write-Host -NoNewLine "Waiting to finish timer job $JobFullName"        
				while ((Get-SPTimerJob $JobFullName) -ne $null)
				{
					Write-Host -f Green -NoNewLine .
					Start-Sleep -Seconds 3				
				}
				Write-Host  -f Green "....Done"		
			}
			Start-Sleep -Seconds 3		
		}
	}
	Write-Footer ".... All Done"
}

#Checks for solutions that failed to deploy, and force them
function Check-FailedJobs ($webAppUrl)
{
	$solList = Get-SPSolution | where {$_.Name -like "tesco.cpservicecentre.*" -and $_.Deployed -eq $false} 
	if ($solList -ne $null)
	{
		Write-Host -f Green "Some solutions didn't deploy - retrying"
		foreach ($sol in $solList)
		{
			Write-Host "Re-installing solution $sol.Name"
			if ($sol.ContainsWebApplicationResource)
			{
				Install-SPSolution -Identity $sol.Name -GACDeployment -WebApplication $webAppUrl -Force			
			}
			else
			{
				Install-SPSolution -Identity $sol.Name -GACDeployment -Force	
			}
		}
	}
}

#Features
#########
function Enable-Feature ()
{
	function Enable-SubFeature ($subFeatureList)
	{
		begin {			
			Write-Header "Enabling features"
		}
		
		process {		
			$featureName = $_.Name
			
			$url = $null
			if ($_.Site -ne $null)
			{
				$url = $mySiteUrl+ $_.Site
			}
			else
			{
				$url = $siteColUrl + $_.Site
			}			
						
			$feature = Get-SPFeature -Identity $featureName	-ErrorAction SilentlyContinue 
			if ($feature -ne $null)
			{				
				switch ($feature.Scope)
				{
					"Farm" {
						Write-Host -NoNewLine "$featureName on the farm "
						$activeFeature = Get-SPFeature -Identity $featureName -Farm -ErrorAction SilentlyContinue
						if ($activeFeature -eq $null)
						{
							enable-spfeature -Identity $featureName
							Write-Host "... activated"
						}
						else
						{
							Write-Host "... already active"
						}
					}
					"Site" {
						Write-Host -NoNewLine "$featureName on site collection"						
						$activeFeature = Get-SPFeature -Identity $featureName -Site $url -ErrorAction SilentlyContinue
						if ($activeFeature -eq $null)
						{							
							enable-spfeature -Identity $featureName -url $url
							Write-Host "... activated"
						}
						else
						{
							Write-Host "... already active"
						}
					}
					"Web" {
						Write-Host -NoNewLine "$featureName on site at $url"
						$activeFeature = Get-SPFeature -Identity $featureName -Web $url -ErrorAction SilentlyContinue
						if ($activeFeature -eq $null)
						{											
							enable-spfeature -Identity $featureName -url $url
							Write-Host "... activated on $url"
						}
						else
						{
							Write-Host "... already active"
						}
					}
				}
			}
			else			
			{
				if ($_.Site -eq $null)
				{
					Write-Host -NoNewLine "$featureName "
					enable-spfeature -Identity $featureName -url $url 
					Write-Host "... activated"
				}
				else
				{
					Write-Host -NoNewLine "$featureName "
					enable-spfeature -Identity $featureName -url $url
					Write-Host "... actived on $url"
				}
			}
		}
		
		end {		
			Write-Footer "Features enabled"
			#Flush blob cache
			FlushWebApp	
		}		
	}
	
	
	
	#Do all the features	
	if ($config.SPInstaller.Features.ChildNodes.Count -gt 0)
	{
		$config.SPInstaller.Features.Feature | Enable-SubFeature	
	}	
}

#Sandbox solutions
function Add-Sandbox ($sandboxList)
{
	function Add-SubSandbox ($sandboxList)
	{
		begin {
			Write-Header "Adding sandbox solutions to solution gallery"
		}
		process {
			$sandboxName = $_
			$solXml = $config.SPInstaller.Solutions.Solution | ? {$_.Name -eq $sandboxName}			
			$siteColName = $solXml.SiteCollection
			$siteCol = $config.SPInstaller.Sites.SiteCollection | ? {$_.Name -eq $siteColName}
			$sandbox = Get-SPUserSolution -Identity $sandboxName -Site $siteCol.Url
			if ($sandbox -eq $null)
			{
				Write-Host "Adding sandbox solution $sandboxName to site collection $siteColName"
				Add-SPUserSolution -LiteralPath "$(gl)\$sandboxName" -Site $siteCol.Url
			}
			else
			{
				Write-Host "Sandbox solution $sandboxName is already added to site collection $siteColName"
			}
		}
		end {
			Write-Footer "Sandbox solutions added"
			#Flush blob cache
			FlushWebApp
		}
	}
	
	if ($sandboxList.Count -gt 0)
	{
		$sandboxList | Add-SubSandbox 
	}
}


function Install-Sandbox ($sandboxList, $siteColUrl)
{
	function Install-SubSandbox ($sandboxList)
	{
		begin {
			Write-Header "Installing sandbox solutions to solution gallery"
		}
		process {
			$sandboxName = $_
			$solXml = $config.SPInstaller.Solutions.Solution | ? {$_.Name -eq $sandboxName}			
			$siteColName = $solXml.SiteCollection
			$siteCol = $config.SPInstaller.Sites.SiteCollection | ? {$_.Name -eq $siteColName}
			$sandbox = Get-SPUserSolution -Identity $sandboxName -Site $siteCol.Url
				
			if ($sandbox -ne $null -and $sandbox.Status -eq "Deactivated" )
			{
				Write-Host "Installing sandbox solution $sandboxName to the solution gallery"
				Install-SPUserSolution -Identity $sandboxName -Site $siteCol.Url
			}
			else
			{
				Write-Host "Sandbox solution $sandboxName is already installed on the site collection $siteColName"
			}
		}		
		end {
			Write-Footer "Sandbox solutions installed"
		}
	}
	
	if ($sandboxList.Count -gt 0)
	{
		$sandboxList | Install-SubSandbox 
	}
}

## Sites
##########


function Create-Sites ()
{
	function Create-SiteCollection ()
	{
		begin {
		}
		process {			
			$webAppElement = Get-WebApp($_.WebApplication)		
			$webApp = Get-SPWebApplication $webAppElement.Url			
			$siteColUrl = $webApp.Url.Remove($webApp.Url.length-1) + $_.Url
			# Create the new Site Collection - if it doesn't exist				
			$siteCol =$webApp.Sites | ? {$_.Url -eq $siteColUrl}			
			if ($siteCol -eq $null)
			{
				#Create the new site collection
				Write-Host "Creating the new site collection at $siteColUrl"
				New-SPSite $siteColUrl -Template $_.Template -OwnerAlias $_.Owner
			}
			else
			{
				Write-Host "Using existing site collection at $siteColUrl"
			}
		}
		end {
		}
		
	}
		
	function Create-SubSite ()
	{
		begin {
		}
		process {
			$siteColName = $_.SiteCollection
			$siteCol = $config.SPInstaller.Sites.SiteCollection | ? {$_.Name -eq $siteColName}
			
			$webAppElement = Get-WebApp ($siteCol.WebApplication)
			
			$webApp = Get-SPWebApplication $webAppElement.Url		
			$siteUrl = $webApp.Url.Remove($webApp.Url.length-1) + $_.Url
			# Create the site - if it doesn't exist
			$site = Get-SPWeb -Identity $siteUrl -ErrorAction:SilentlyContinue
			if ($site -eq $null)
			{
				Write-Host "Creating site at $siteUrl"			
				New-SPWeb -Url $siteUrl -Template $_.Template -Confirm:$false
				#-Name "$_.Name" -Description "$_.Description"
			}
			else
			{
				Write-Host "Using existing site at $siteUrl"
			}
		}
		end 
		{
		}
		
	}
	
	if ($config.SPInstaller.Sites.ChildNodes.Count -gt 0)
	{		
		$config.SPInstaller.Sites.SiteCollection | Create-SiteCollection
	}
	if ($config.SPInstaller.Sites.ChildNodes.Count -gt 0)
	{
		$config.SPInstaller.Sites.Site | Create-SubSite
	}
}

function Get-WebApp ($webAppName)
{
	$webApp = $config.SPInstaller.WebApplications.WebApplication | ? {$_.Name -eq $webAppName}
	return $webApp
}

#Writing helper functions

function Write-Header ($comment)
{
	Write-Host ""
	Write-Host -f Green "$comment"	
}

function Write-Footer ($comment)
{	
	Write-Host -f Yellow "$comment"	
	Write-Host ""
}

Export-ModuleMember -Function Install-Project, Write-Header, Write-Footer

